/* Copyright   1991 Gustavus Adolphus College.  All rights reserved.
 *
 * Schematik was developed by Gustavus Adolphus College (GAC) with
 * support from NeXT Computer, Inc.  Permission to copy this software,
 * to redistribute it, and to use it for any purpose is granted,
 * subject to the following restrictions and understandings.
 *
 * 1. Any copy made of this software must include this copyright
 * notice in full.
 *
 * 2. Users of this software agree to make their best efforts (a) to
 * return to the GAC Mathematics and Computer Science Department any
 * improvements or extensions that they make, so that these may be
 * included in future releases; and (b) to inform GAC of noteworthy
 * uses of this software.
 *
 * 3. All materials developed as a consequence of the use of this
 * software shall duly acknowledge such use, in accordance with the
 * usual standards of acknowledging credit in academic research.
 *
 * 4. GAC makes no express or implied warranty or representation of
 * any kind with respect to this software, including any warranty
 * that the operation of this software will be error-free.  ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
 * PURPOSE IS HEREBY DISCLAIMED.  GAC is under no obligation to
 * provide any services, by way of maintenance, update, or otherwise.
 *
 * 5. In conjunction with products arising from the use of this
 * material, there shall be no use of the name of Gustavus Adolphus
 * College nor of any adaptation thereof in any advertising,
 * promotional, or sales literature without prior written consent
 * from GAC in each case.
 */

#import "Main.h"
#import "../defines.h"
#import InfoAgent_h
#import HelpAgent_h
#import PrefAgent_h
#import FindAgent_h
#import InteractionWin_h
#import DocText_h
#import SchemeProtocol_h
#import GraphicsView_h
#import <appkit/FontManager.h>
#import <appkit/Listener.h>
#import <appkit/Speaker.h>
#import <appkit/Matrix.h>
#import <appkit/NXCursor.h>
#import <appkit/OpenPanel.h>
#import <appkit/PrintInfo.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <appkit/publicWraps.h>
#import <dpsclient/wraps.h>
#import <objc/HashTable.h>
#import <objc/List.h>
#import <sys/signal.h>
#import <mach.h>
extern void NXBeep(void);

#define ALTARROWCURSORTIFF	"altcursor1"
#define ALTIBEAMCURSORTIFF	"altcursor1"
#define ALTCURSORHOTSPOT	{7,7}
#define NEW_ROTATENUM		5

static int numDocsOpened=0;
static BOOL terminus=NO;

@implementation Main

+ new
{
DEBUG_FUNC1(DEBUGLEVEL);
    self = [super new];
    prefAgent = [PrefAgent new];
    if ([prefAgent useSpecialCursors])
      {
        NXPoint p=ALTCURSORHOTSPOT;
	NXArrow = [[NXCursor alloc] initFromImage:[[NXImage alloc] initFromSection:ALTARROWCURSORTIFF]];
	[NXArrow setHotSpot:&p];
	NXIBeam = [[NXCursor alloc] initFromImage:[[NXImage alloc] initFromSection:ALTIBEAMCURSORTIFF]];
	[NXIBeam setHotSpot:&p];
      }
    interactionWindow = [InteractionWin new];
    [interactionWindow makeKeyAndOrderFront:self];
    protocolObj = [[SchemeProtocol allocFromZone:[self zone]] init];
    infoAgent = [InfoAgent new];
    helpAgent = [HelpAgent new];
    findAgent = [FindAgent new];
    [protocolObj showRunState:INPUTSTATE];
    [[NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES];
    [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
    [[NXApp printInfo] setHorizCentered:NO];
    [[NXApp printInfo] setVertCentered:NO];
    [FontManager setFontPanelFactory:[MyFontPanel class]];
    graphicsZone = NXCreateZone(vm_page_size, vm_page_size, YES);
    graphicsViews = [[HashTable allocFromZone:graphicsZone] initKeyDesc:"i" valueDesc:"@"];
    return self;
}

- info:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [infoAgent activateAgent:[sender selectedCell]];    
}

- help:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [helpAgent activateAgent:[sender selectedCell]];    
}

- manual:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [helpAgent activateAgent:[sender selectedCell]];    
}

- preferences:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [prefAgent activateAgent:[sender selectedCell]];    
}

- newDocument:sender
{
    id new=[DocWin new];
    NXPoint p,q;

DEBUG_FUNC1(DEBUGLEVEL);
    [[prefAgent getDocOrigin:&p] getDocOffset:&q];
    [new moveTo:(p.x+q.x*(numDocsOpened%NEW_ROTATENUM)) :(p.y+q.y*(numDocsOpened%NEW_ROTATENUM))];
    numDocsOpened++;
    return [new makeKeyAndOrderFront:self];
}

- openDocument:sender
{
    id new=[DocWin performOpenFromFile:NULL];
    NXPoint p,q;

DEBUG_FUNC1(DEBUGLEVEL);
    if (new==nil) return self;
    [[prefAgent getDocOrigin:&p] getDocOffset:&q];
    [new moveTo:(p.x+q.x*(numDocsOpened%NEW_ROTATENUM)) :(p.y+q.y*(numDocsOpened%NEW_ROTATENUM))];
    numDocsOpened++;
    return [new makeKeyAndOrderFront:self];
}

- find:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [findAgent activateAgent:[sender selectedCell]];
}

- findNext:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [findAgent findForward:[sender selectedCell]];
}

- findPrevious:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [findAgent findBackward:[sender selectedCell]];
}

- evaluate:sender
{
    NXSelPt start,end;
    char *theSelection;
    id text;

DEBUG_FUNC1(DEBUGLEVEL);
    text = [[NXApp mainWindow] textView];
    if (text==nil) return self;
    switch ([[sender selectedCell] tag])
      {
        case MENU_ACTIONS_Evaluate: if (*(theSelection = [text getSelection]))
				      [protocolObj send:theSelection];
				    else
				      {
					[text getSel:&start :&end];
					if (![text selectScope])
					  {
					    NXBeep();
					    break;
					  }
					NXPing();
					[protocolObj send:(theSelection = [text getSelection])];
					if ([[NXApp mainWindow] isMemberOf:[DocWin class]])
					  [text setSel:start.cp :end.cp];
				      }
				    [protocolObj send:"\n"];
				    if ([prefAgent transcriptMode])
                                      {
				        [text appendNewlineIfNeeded];
                                        [protocolObj sendOutput:theSelection];
				      }
				    break;
	case MENU_ACTIONS_EvalAll: [text getSel:&start :&end];
				   [protocolObj send:(theSelection = [text getAll])];
				   if ([[NXApp mainWindow] isMemberOf:[DocWin class]])
				     [text setSel:start.cp :end.cp];
				   [protocolObj send:"\n"];
				    if ([prefAgent transcriptMode])
                                      {
				        [text appendNewlineIfNeeded];
                                        [protocolObj sendOutput:theSelection];
				      }
				   break;

      }
    return self;
}

- abort:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    switch ([[sender selectedCell] tag])
      {
        case MENU_ACTIONS_AbortTop  : [protocolObj sendSignal:SIGNALABORTTOP]; break;
	case MENU_ACTIONS_AbortSame : [protocolObj sendSignal:SIGNALABORTSAME]; break;
	case MENU_ACTIONS_AbortPrev : [protocolObj sendSignal:SIGNALABORTPREV]; break;
        case MENU_ACTIONS_Breakpoint: [protocolObj sendSignal:SIGNALBREAKPOINT]; break;
      }
    return self;
}

- print:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return [keyWindow print:sender];
}

- (id)findAgent		{ return findAgent;}
- (id)helpAgent		{ return helpAgent;}
- (id)prefAgent		{ return prefAgent;}
- (id)infoAgent		{ return infoAgent;}
- (id)interactionWindow	{ return interactionWindow;}
- (id)protocolObj	{ return protocolObj;}

- openGraphicsWindow:(unsigned)windowNum width:(unsigned)width height:(unsigned)height
{
    NXRect r={{0.0,0.0},{width,height}};
    id newGraphics=[[GraphicsView allocFromZone:graphicsZone] initFrame:&r window:windowNum];
DEBUG_FUNC1(DEBUGLEVEL);
    if (newGraphics==nil)
      return nil;
    [graphicsViews insertKey:(const void *)windowNum value:(void *)newGraphics];
    return self;
}

- closeGraphicsWindow:(unsigned)windowNum
{
DEBUG_FUNC1(DEBUGLEVEL);
    [[(id)[graphicsViews valueForKey:(const void *)windowNum] window] setCloseEnabled];
    [graphicsViews removeKey:(const void *)windowNum];
    return self;
}

- setGraphicsWindowFlush:(unsigned)windowNum mode:(unsigned)mode
{
DEBUG_FUNC1(DEBUGLEVEL);
    [(id)[graphicsViews valueForKey:(const void *)windowNum] setFlushMode:mode];
    return self;
}

- printGraphicsWindow:(unsigned)windowNum
{
DEBUG_FUNC1(DEBUGLEVEL);
    [(id)[graphicsViews valueForKey:(const void *)windowNum] printPSCode:self];
    return self;
}

@end

@implementation Main (Subclass)

- restartSubprocess
{
    [[protocolObj terminate:self withPrejudice:YES] free];
    protocolObj = [[SchemeProtocol allocFromZone:[self zone]] init];
    if (protocolObj==nil)
      NXRunAlertPanel(SCHEMEPROCMESSAGE,PROCESSRESTARTBAD,NULL,NULL,NULL);
    else
      NXRunAlertPanel(SCHEMEPROCMESSAGE,PROCESSRESTARTOK,NULL,NULL,NULL);
    return self;
}

- sendEvent:(NXEvent *)theEvent
{
    if ((theEvent->type==NX_KEYDOWN)&&(theEvent->data.key.keyCode==73)&&(theEvent->flags&NX_NEXTCTRLKEYMASK)&&(theEvent->flags&NX_NEXTLALTKEYMASK))
      [self restartSubprocess];
    return [super sendEvent:theEvent];
}

@end

@implementation Main (Delegate)

- (BOOL)appAcceptsAnotherFile:sender
{
DEBUG_FUNC1(DEBUGLEVEL);
    return YES;
}

- (BOOL)app:sender openFile:(const char *)file type:(const char *)aType
{
    id new;
    NXPoint p,q;

DEBUG_FUNC1(DEBUGLEVEL);
    if (!file||((new=[DocWin performOpenFromFile:(char *)file])==nil))
      return NO;
    [[prefAgent getDocOrigin:&p] getDocOffset:&q];
    [new moveTo:(p.x+q.x*(numDocsOpened%NEW_ROTATENUM)) :(p.y+q.y*(numDocsOpened%NEW_ROTATENUM))];
    numDocsOpened++;
    [new makeKeyAndOrderFront:self];
    return YES;
}

- _saveUnsaved
{
    id *clist=NX_ADDRESS(windowList);
    id docwins=[DocWin class];
    int i;

    for(i=[windowList count]; i--;)
      if ([clist[i] isDocEdited]&&[clist[i] isMemberOf:docwins])
	{
	  switch (NXRunAlertPanel(QUITALERTTITLE, QUITALERTMESSAGE, QUITALERTDEFAULT, QUITALERTALTERNATE, QUITALERTOTHER))
	    {
	      case NX_ALERTOTHER    : return nil;
	      case NX_ALERTDEFAULT  : clist=NX_ADDRESS(windowList);
                                      for(i=[windowList count]; i--;)
				        if ([clist[i] isMemberOf:docwins]&&![[clist[i] makeKeyAndOrderFront:self] performClose:self])
					  return nil;
	    }
	  break;
	}
    if ([interactionWindow isDocEdited]&&(i<0))
      [interactionWindow makeKeyAndOrderFront:self];
      switch (NXRunAlertPanel(QUITALERTTITLE, QUITALERTMESSAGE2, SAVEALERTDEFAULT, SAVEALERTALTERNATE, SAVEALERTOTHER))
	{
	  case NX_ALERTOTHER  : return nil;
	  case NX_ALERTDEFAULT: [interactionWindow performSave:self];
	}
    return self;
}

- app:sender powerOffIn:(int)ms andSave:(int)anInt
{
    port_t port=NXPortFromName(NX_WORKSPACEREQUEST, NULL);
    int actual;

    if ((port!=PORT_NULL)&&[prefAgent confirmOnQuit])
      {
        [appSpeaker setSendPort:port];
	if (![appSpeaker extendPowerOffBy:600000 actual:&actual])
          {
	    BOOL confirm = [prefAgent confirmOnClose];
	    terminus = YES;
	    if (!confirm)
	      [prefAgent setConfirmOnClose:YES];
	    [self _saveUnsaved];
	    if (!confirm)
	      [prefAgent setConfirmOnClose:NO];
	  }
      }
    [protocolObj terminate:self withPrejudice:YES];
    return self;
}

- appWillTerminate:sender
{
    BOOL confirm = [prefAgent confirmOnClose];

    if (!confirm)
      [prefAgent setConfirmOnClose:YES];
    if ([prefAgent confirmOnQuit]&&([self _saveUnsaved]==nil))
      {
        if (!confirm)
          [prefAgent setConfirmOnClose:NO];
        return nil;
      }
    terminus = YES;
    [protocolObj terminate:self withPrejudice:YES];
    return self;
}

@end

@implementation MyFontPanel

+ newContent:(const NXRect *)contentRect style:(int)aStyle backing:(int)bufferingType buttonMask:(int)mask defer:(BOOL)flag
{
    int i;
    
DEBUG_FUNC1(DEBUGLEVEL);
    self = [super newContent:contentRect style:aStyle backing:bufferingType buttonMask:mask defer:flag];
    for(i=([families cellCount]-1); i>=0; i--)
      if (NXOrderStrings((unsigned char *)[[families cellAt:i :0] stringValue], (unsigned char *)GOODFONT1, YES, -1, NULL)
          &&NXOrderStrings((unsigned char *)[[families cellAt:i :0] stringValue], (unsigned char *)GOODFONT2, YES, -1, NULL))
        [families removeRowAt:i andFree:YES];
    [families sizeToCells];
    selFont = [[[NXApp mainWindow] textView] font];
    selMetrics = [selFont metrics];
    return self;
}

@end

@implementation Window (PrintingExt)

- print:sender
{
    id keyWindow = [NXApp keyWindow];
    if ([[keyWindow contentView] hasSubviewOfKind:[GraphicsView class]])
      [[[keyWindow contentView] docView] printPSCode:self];
    else if ([[keyWindow contentView] hasSubviewOfKind:[ScrollView class]])
      {
        if (!NXOrderStrings((unsigned char *)[keyWindow title],(unsigned char *)LICENSEPANELTITLE,YES,-1,NULL))
	  [[[[[keyWindow contentView] subviews] objectAt:0] docView] printPSCode:self];
	else if (!NXOrderStrings((unsigned char *)[keyWindow title],(unsigned char *)HELPPANELTITLE,YES,-1,NULL))
	  [[[[keyWindow delegate] helpScrollView] docView] printPSCode:self];
      }
    return self;
}

@end

@implementation View (PrintingExt)

- (BOOL)hasSubviewOfKind:aClass
{
    int i;
    if ([self isKindOf:aClass])
      return YES;
    else if ([self subviews]==nil)
      return NO;
    else
      for(i=0; i<[subviews count]; i++)
        if ([[subviews objectAt:i] hasSubviewOfKind:aClass])
	  return YES;
    return NO;
}

@end
